package it.unibo.convoycruisecontrol.boatsubsystem;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import it.unibo.contact.boatSubSystem.BoatSubSystemMain;
import it.unibo.contact.platformuv.RunTimeKb;
import it.unibo.location.LocationDevice;
import it.unibo.util.information.InformationConverter;
import it.unibo.util.information.PrologInformationConverter;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public abstract class BoatSubSystemLauncher extends BoatSubSystemMain 
{
	protected AbstractCommandBoat boat;
	protected IDashboard dashboard;

	protected int DT;
	protected int refreshPeriod = 3000;

	protected IBoatFactory boatFactory;
	protected LocationDevice locDev;
	protected String boatAddress;
	protected int boatPortNumber;
	protected String chiefAddress;
	protected int chiefPortNumber;
	protected int boatNumber;
	protected String dspaceAddress;
	protected int dspacePort;
	
	protected InformationConverter informationConverter = new PrologInformationConverter();

	@Override
	public void doJob() 
	{
		initProperty();
		init();
		configure();
		initGui();
		start();
	}
	
	@Override
	protected void configureSystem()
	{
		RunTimeKb.init(view);
		//Protocols for application messages
		RunTimeKb.addSubject("TCP", "space","coreCmd", dspaceAddress, dspacePort);	
		RunTimeKb.addSubject("TCP", "space","outCmd", dspaceAddress, dspacePort);	
		RunTimeKb.addSubject("TCP" , "statusSupervisor" , "enabled",chiefAddress, chiefPortNumber );   	
		RunTimeKb.addSubject("TCP" , "mapManager" , "navigationInfo",chiefAddress, chiefPortNumber );   	
		RunTimeKb.addSubject("TCP" , "distanceSupervisor" , "navigationInfo",chiefAddress, chiefPortNumber );   	
		RunTimeKb.addSubject("TCP" , "startSupervisor" , "navigationInfo",chiefAddress, chiefPortNumber );   	
		RunTimeKb.addSubject("TCP" , "timingSupervisor" , "navigationInfo",chiefAddress, chiefPortNumber );  

		RunTimeKb.addSubject("TCP" , "boat"+boatNumber , "start",boatAddress,boatPortNumber );   
		RunTimeKb.addSubject("TCP" , "boat"+boatNumber , "stop",boatAddress,boatPortNumber ); 

		//Application messages
		RunTimeKb.addInputConnMsg( "coreCmd", true); //system request
		RunTimeKb.addInputConnMsg( "enabled", true);
		RunTimeKb.addInputConnMsg( "navigationInfo", true);
		RunTimeKb.addInputConnMsg( "start", true);
		RunTimeKb.addInputConnMsg( "stop", true);
	}

	@Override
	protected void configureSubjects(){
		try 
		{	
			boat = boatFactory.createBoat(locDev,boatNumber);
			boat.setName("boat"+boatNumber);
			boat.setEnv(null);
			boat.initInputSupports();

			dashboard = boatFactory.createDashboard();
			dashboardManager = new DashboardManager(dashboard, boat.getNavigationInfo(), locDev, refreshPeriod, informationConverter);
			dashboardManager.setName("dashboardManager"+boatNumber);
			dashboardManager.setEnv(null);
			dashboardManager.initInputSupports();

			sendingNavigationInfo = new SendingNavigationInfo(DT, boat.getNavigationInfo(), informationConverter);
			sendingNavigationInfo.setName("sendingNavigationInfo"+boatNumber);
			sendingNavigationInfo.setEnv(null);
			sendingNavigationInfo.initInputSupports();

			sensingBoatStatus = new SensingBoatStatus(informationConverter);
			sensingBoatStatus.setName("sensingBoatStatus"+boatNumber);
			sensingBoatStatus.setEnv(null);
			sensingBoatStatus.initInputSupports();

			boat.getStatusBoat().addBoatStatusListener((BoatStatusListener) sensingBoatStatus);

			registerObservers();
		}
		catch(Exception e){e.printStackTrace();}
	}

	public void initConvoyInformations(File xmlFile, int boatNumber)
	{
		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder = null;
		Document doc = null;
		
		try {
			docBuilder = docBuilderFactory.newDocumentBuilder();
			doc = docBuilder.parse (xmlFile);
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// normalize text representation
		doc.getDocumentElement ().normalize();

		int chiefSeqNum = Integer.parseInt(doc.getDocumentElement().getAttribute("chief"));
		DT = Integer.parseInt(doc.getDocumentElement().getAttribute("DT"));
		
		Element dspaceElement = (Element) doc.getElementsByTagName("dspace").item(0);
		dspaceAddress = dspaceElement.getAttribute("address");
		dspacePort = Integer.parseInt(dspaceElement.getAttribute("port"));
		
		NodeList listOfBoats = doc.getElementsByTagName("boat");

		for(int i=0; i < listOfBoats.getLength() ; i++)
		{
			Node boatNode = listOfBoats.item(i);
			Element boatElement = null;
			if(boatNode.getNodeType() == Node.ELEMENT_NODE)
			{
				boatElement = (Element)boatNode;

				int seqNum = Integer.parseInt(boatElement.getAttribute("seqNum"));
				if(seqNum == boatNumber)	// Ho trovato la configurazione per la barca corrente
				{
					this.boatNumber = boatNumber;
					this.boatFactory = getBoatFactory(boatElement.getAttribute("factoryClass"));
					this.boatAddress = boatElement.getAttribute("address");
					this.boatPortNumber = Integer.parseInt(boatElement.getAttribute("port"));
					this.locDev = getLocationDevice(boatElement.getAttribute("locationDevice"));
				}

				if(seqNum == chiefSeqNum)		// Ho trovato la barca del chief -> prendo i suoi dati
				{
					this.chiefAddress = boatElement.getAttribute("address");
					this.chiefPortNumber = Integer.parseInt(boatElement.getAttribute("port"));
				}
			}
		}
	}

	@Override
	protected void start() 
	{
		boat.start();
		super.start();
	}

	protected abstract IBoatFactory getBoatFactory(String className);
	protected abstract LocationDevice getLocationDevice(String deviceName);

	protected abstract void initGui();
}
